package it.uniroma3.dia.ia.textcategorization.texcat;

import it.uniroma3.dia.ia.textcategorization.benchmark.Benchmark;
import it.uniroma3.dia.ia.textcategorization.benchmark.Report;
import it.uniroma3.dia.ia.textcategorization.categorizer.Categorizer;
import it.uniroma3.dia.ia.textcategorization.categorizer.CategorizerBuilder;
import it.uniroma3.dia.ia.textcategorization.categorizer.CategorizerBuilderFactory;
import it.uniroma3.dia.ia.textcategorization.datasource.DataSource;
import it.uniroma3.dia.ia.textcategorization.datasource.DataSourceFactory;
import it.uniroma3.dia.ia.textcategorization.model.Category;
import it.uniroma3.dia.ia.textcategorization.model.Text;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;

/**
 * 
 * This class allows to execute a demo of TexCat's features.
 * Given a file describing a data set, TexCatDemo retrieves
 * the texts and executes the training and the benchmarking.
 * Finally, it ouputs some statistics about the categorizer.
 * 
 * @author Marco Liceti
 *
 */
public final class TexCatDemo {

	private DataSource dataSource;
	private CategorizerBuilder categorizerBuilder;
	private Set<Category> categories;
	private Map<Category, Integer> trainingRequests;
	private Map<Category, Integer> testRequests;

	/**
	 * 
	 * Creates a new instance of TexCatDemo by parsing
	 * the file provided.
	 * 
	 * @param file The file describing the data set in the
	 * form of a sequence of lines, each formed by a textual
	 * representation of a category followed by two integers,
	 * representing the numbers of texts to be used for training
	 * and testing phases.
	 * 
	 * @throws IOException
	 */
	public TexCatDemo(File file) throws IOException {
		this.dataSource = DataSourceFactory.newDataSource();
		this.categorizerBuilder = CategorizerBuilderFactory
				.newCategorizerBuilder();
		this.categories = new HashSet<Category>();
		this.trainingRequests = new HashMap<Category, Integer>();
		this.testRequests = new HashMap<Category, Integer>();

		if (file.exists()) {
			BufferedReader reader = new BufferedReader(new InputStreamReader(
					new FileInputStream(file)));
			String line = reader.readLine();
			while (line != null) {
				Scanner scanner = new Scanner(line);
				Category category = Category.valueOf(scanner.next());
				int training = scanner.nextInt();
				int test = scanner.nextInt();

				this.categories.add(category);
				this.trainingRequests.put(category, training);
				this.testRequests.put(category, test);

				line = reader.readLine();
			}
			reader.close();
		}
	}

	/**
	 * 
	 * Executes this demo instance.
	 * 
	 */
	public void execute() {
		System.out.println();
		System.out.println("* TexCat Demo start!");
		System.out.println();
		System.out.println("* Start of training set and test set building.");
		System.out.println();

		Map<Text, Category> trainingSet = new HashMap<Text, Category>();
		Map<Text, Category> testSet = new HashMap<Text, Category>();
		buildTrainingAndTestSet(this.dataSource, trainingSet, testSet);

		System.out.println("* Training set and test set building completed.");
		System.out.println();
		System.out.println("* Starting categorizer's training.");
		System.out.println();

		Categorizer categorizer = this.categorizerBuilder.newCategorizer(
				this.categories, trainingSet);

		System.out.println();
		System.out.println("* Training completed.");
		System.out.println();
		System.out.println("* Starting categorizer's benchmarking.");

		Report report = Benchmark.getReport(categorizer, testSet);

		System.out.println("* Benchmarking completed.");
		System.out.println();
		System.out.println("* Categorizer performances:");
		System.out.println();
		System.out.println(report);
	}

	private void buildTrainingAndTestSet(DataSource dataSource,
			Map<Text, Category> trainingSet, Map<Text, Category> testSet) {
		for (Category category : this.categories) {
			int training = this.trainingRequests.get(category);
			int test = this.testRequests.get(category);
			int requested = training + test;

			System.out.println("* " + requested
					+ " text(s) requested for category " + category.toString()
					+ ".");

			Set<Text> texts = dataSource.getTexts(category, requested);
			int retrieved = texts.size();
			while (retrieved < requested) {
				texts = dataSource.getTexts(category, requested);
			}

			System.out.println("* Text(s) retrieved.");
			System.out.println("* Text(s) to be used for training: " + training
					+ ".");
			System.out.println("* Text(s) to be used for test: " + test + ".");
			System.out.println();

			fillTrainingAndTestSets(trainingSet, testSet, category, texts,
					training);
		}
	}

	private void fillTrainingAndTestSets(Map<Text, Category> trainingSet,
			Map<Text, Category> testSet, Category category, Set<Text> texts,
			long numberOfTrainingInstances) {
		long numberOfTextsVisited = 0;
		for (Text text : texts) {
			if (numberOfTextsVisited < numberOfTrainingInstances) {
				trainingSet.put(text, category);
				numberOfTextsVisited++;
			} else {
				testSet.put(text, category);
			}
		}
	}

}
